home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 050 / madtrb1.arc / SCREEN.INC < prev    next >
Text File  |  1986-03-03  |  48KB  |  1,018 lines

  1. { SCREEN.INC }
  2.  
  3. { *************************************************************************** }
  4. { *                                                                         * }
  5. { *                TURBO SCREEN INPUT PRE-PROCESSOR TOOLKIT                 * }
  6. { *                                                                         * }
  7. { *                 SCREEN FUNCTION SUBPROGRAM INCLUDE FILE                 * }
  8. { *                                                                         * }
  9. { *                             Version  1.07                               * }
  10. { *                                                                         * }
  11. { *                                                                         * }
  12. { *     This subprogram contains various functions and procedures to        * }
  13. { *     manipulate the monitor screen, get date and time, etc.              * }
  14. { *     The following functions and procedures are contained in             * }
  15. { *     this subprogram:                                                    * }
  16. { *                                                                         * }
  17. { *                   MonitorType              ( IBM Specific )             * }
  18. { *                   SpeedPrint               ( IBM Specific )             * }
  19. { *                   SpeedWrite               ( IBM Specific )             * }
  20. { *                   SpeedPrint2              ( IBM Specific )             * }
  21. { *                   DrawWindow1                                           * }
  22. { *                   DrawWindow2                                           * }
  23. { *                   ZoomWindow1                                           * }
  24. { *                   ZoomWindow2                                           * }
  25. { *                   DrawHorizWindowLine1                                  * }
  26. { *                   DrawHorizWindowLine2                                  * }
  27. { *                   WriteCenterText                                       * }
  28. { *                   SetCursorSize            ( IBM Specific )             * }
  29. { *                   HideBlinkingCursor       ( IBM Specific )             * }
  30. { *                   ShowBlinkingCursor       ( IBM Specific )             * }
  31. { *                   InitTextScreenPointers   ( IBM Specific )             * }
  32. { *                   StoreTextScreen          ( IBM Specific )             * }
  33. { *                   RecallTextScreen         ( IBM Specific )             * }
  34. { *                   WriteScreenPageToFile    ( IBM Specific )             * }
  35. { *                   ReadScreenPagesFromFile  ( IBM Specific )             * }
  36. { *                   DisplayScreenPage        ( IBM Specific )             * }
  37. { *                   SoundError                                            * }
  38. { *                   SoundAttention                                        * }
  39. { *                   WaitUntilKeypressed                                   * }
  40. { *                   Date1                    ( IBM Specific )             * }
  41. { *                   Date2                    ( IBM Specific )             * }
  42. { *                   Time                     ( IBM Specific )             * }
  43. { *                                                                         * }
  44. { *     Note to reduce the size of your compiled code, remove those         * }
  45. { *     procedures within this subprogram that you are not using.           * }
  46. { *                                                                         * }
  47. { *************************************************************************** }
  48.  
  49.  
  50.  
  51. Function MonitorType:Integer;
  52.  
  53. { This function returns an integer value corresponding to the current display
  54.   mode of the video monitor.  IBM specific.  Typical values follow:
  55.  
  56.   VALUE   MODE          SIZE        ADAPTER          MONITOR      COLORS
  57.  
  58.     0     Text          40  x 25    CGA,EGA,PCjr     Monochrome   16 (grey)
  59.     1     Text          40  x 25    CGA,EGA,PCjr     Color        16 foreground, 8 background
  60.     2     Text          80  x 25    CGA,EGA,PCjr     Monochrome   16 (grey)
  61.     3     Text          80  x 25    CGA,EGA,PCjr     Color        16 foreground, 8 background
  62.     4     Graphics      320 x 200   CGA,EGA,PCjr     Color        4
  63.     5     Graphics      320 x 200   CGA,EGA,PCjr     Monochrome   4 (grey)
  64.     6     Graphics      640 x 200   CGA,EGA,PCjr     Color        2
  65.     7     Text          80  x 25    EGA,Monochrome   Monochrome   b/w
  66.     8     Graphics      160 x 200   PCjr             Color        16
  67.     9     Graphics      320 x 200   PCjr             Color        16
  68.     10    Graphics      640 x 200   PCjr             Color        4
  69.     11    EGA Internal
  70.     12    EGA Internal
  71.     13    Graphics      320 x 200   EGA              Color        16
  72.     14    Graphics      640 x 200   EGA              Color        16
  73.     15    Graphics      640 x 350   EGA              Monochrome   b/w
  74.     16    Graphics      640 x 350   EGA              Color        64  }
  75.  
  76. Begin   { MonitorType }
  77.   MonitorType:=Mem[$0040:$0049];       { Get the current video mode value }
  78. End;    { MonitorType }
  79.  
  80.  
  81.  
  82. Procedure SpeedPrint(    Text:WorkString;
  83.                          Col,
  84.                          Row :Integer);
  85.  
  86. { This procedure writes the passed text directly to the video memory.
  87.   This allows for ultra-fast screen output.
  88.  
  89.       Text = any literal string up to 80 characters long to be printed
  90.       Col  = screen column location ( 1 to 80 )
  91.       Row  = screen row location    ( 1 to 25 )
  92.  
  93.  
  94.   Note that this procedure does not clip the passed text if it extends beyond
  95.   the edge of the screen, rather it will wrap the text around to the next
  96.   line.  IBM specific.  This routine does cause some colored snow on the
  97.   IBM standard color monitor, but not on the monochrome or enhanced monitors.
  98.  
  99.   I would like to note that I have encountered some erratic behavior from this
  100.   procedure after doing a complex windowing call on an enhanced color monitor
  101.   and adapter.  Typically this procedure will not work immediately after
  102.   displaying a window or zoom window.  I think it is because the video buffer
  103.   resides at a different location than specified below. }
  104.  
  105.  
  106. Var
  107.   ColorMonitorImage:Array[1..25,1..80,1..2] Of Char Absolute $B800:0000;
  108.     { an overlayed map of the color video memory addresses }
  109.   MonoMonitorImage:Array[1..25,1..80,1..2] Of Char Absolute $B000:0000;
  110.     { an overlayed map of the monochrome video memory addresses }
  111.   ScreenCol:Integer;                   { an index used to help write out the passed character string }
  112.  
  113. Begin   { SpeedPrint }
  114.   If MonitorType=7 Then                { monochrome monitor and monochrome adapter }
  115.     For ScreenCol:=1 To Length(Text) Do
  116.       MonoMonitorImage[Row,Col+ScreenCol-1,1]:=Text[ScreenCol]
  117.   Else                                 { color or monochrome monitor and color adapter }
  118.     For ScreenCol:=1 To Length(Text) Do
  119.       ColorMonitorImage[Row,Col+ScreenCol-1,1]:=Text[ScreenCol];
  120. End;    { SpeedPrint }
  121.  
  122.  
  123.  
  124. Procedure SpeedWrite(    Text:WorkString);
  125.  
  126. { This procedure writes the passed text directly to the video memory wherever
  127.   the cursor is located.  This allows for ultra-fast screen output.  Note that
  128.   this procedure does not clip the passed text if it extends beyond the edge
  129.   of the screen, rather it will wrap the text around to the next line.  IBM
  130.   specific.  This routine does cause some colored snow on the IBM standard
  131.   color monitor, but not on the monochrome or enhanced monitors.
  132.  
  133.   I would like to note that I have encountered some erratic behavior from this
  134.   procedure after doing a complex windowing call on an enhanced color monitor
  135.   and adapter.  Typically this procedure will not work immediately after
  136.   displaying a window or zoom window.  I think it is because the video buffer
  137.   resides at a different location than specified below. }
  138.  
  139. Var
  140.   ColorMonitorImage:Array[1..25,1..80,1..2] Of Char Absolute $B800:0000;
  141.     { an overlayed map of the color video memory addresses }
  142.   MonoMonitorImage:Array[1..25,1..80,1..2] Of Char Absolute $B000:0000;
  143.     { an overlayed map of the monochrome video memory addresses }
  144.   CurrentCursorCol:Integer;            { a variable used in incrementing the cursor position as each character is written }
  145.   CurrentCursorRow:Integer;            { a variable used in incrementing the cursor position as each character is written }
  146.   ScreenCol:Integer;                   { an index used to help write out the passed character string }
  147.  
  148. Begin   { SpeedWrite }
  149.   CurrentCursorCol:=WhereX;
  150.   CurrentCursorRow:=WhereY;
  151.   If MonitorType=7 Then                { monochrome monitor and monochrome adapter }
  152.     For ScreenCol:=1 To Length(Text) Do
  153.       MonoMonitorImage[CurrentCursorRow,CurrentCursorCol+ScreenCol-1,1]:=Text[ScreenCol]
  154.   Else                                 { color or monochrome monitor and color adapter }
  155.     For ScreenCol:=1 To Length(Text) Do
  156.       ColorMonitorImage[CurrentCursorRow,CurrentCursorCol+ScreenCol-1,1]:=Text[ScreenCol];
  157.   CurrentCursorCol:=CurrentCursorCol+Length(Text); { increment cursor position }
  158.   GotoXY(CurrentCursorCol,CurrentCursorRow);
  159. End;    { SpeedWrite }
  160.  
  161.  
  162.  
  163. Procedure SpeedPrint2(Var Text:WorkString;
  164.                       Var Col,
  165.                           Row :Integer);
  166.  
  167. { This procedure writes text directly to video memory.  This allows for
  168.   ultra-fast screen output.  IBM specific.
  169.  
  170.          Text = any literal string up to 80 sharacters long to be printed
  171.          Col  = screen col location ( 1 to 80 )
  172.          Row  = screen row location ( 1 to 25 )
  173.  
  174.   Note that this procedure does not clip the literal string if it extends
  175.   beyond the screen edge, it will wrap to the next line.  Also, WorkString
  176.   can be dimensioned larger than 80 if desired.
  177.  
  178.   This procedure is nicer than SpeedPrint since this procedure does not
  179.   produce colored snow on the standard IBM color monitor when it is called.
  180.   This is because this procedure waits for the horizontal retrace check.
  181.   I should note that colored snow only occurs on the standard color monitor,
  182.   not the monochrome or enhanced color monitors.  Both routines are fairly
  183.   equal in speed.
  184.  
  185.   I would like to note that I have encountered some erratic behavior from this
  186.   procedure after doing a complex windowing call on an enhanced color monitor
  187.   and adapter.  Typically this procedure will not work immediately after
  188.   displaying a window or zoom window.  I think it is because the video buffer
  189.   resides at a different location than specified below. }
  190.  
  191.  
  192. Begin   { SpeedPrint2 }
  193.   InLine($8B/$5E/$08/                  {                mov     bx,[bp+8]      ; address of col var       }
  194.          $8B/$3F/                      {                mov     di,[bx]        ; get the col value        }
  195.          $4F/                          {                dec     di                                        }
  196.          $8B/$5E/$04/                  {                mov     bx,[bp+4]      ; address of row var       }
  197.          $8B/$07/                      {                mov     ax,[bx]        ; get the row value        }
  198.          $48/                          {                dec     ax                                        }
  199.          $8B/$5E/$0C/                  {                mov     bx,[bp+12]     ; address of string        }
  200.          $32/$ED/                      {                xor     ch,ch                                     }
  201.          $8A/$0F/                      {                mov     cl,[bx]        ; get string length        }
  202.          $80/$F9/$00/                  {                cmp     cl,0           ; test for null string     }
  203.          $74/$40/                      {                je      exit                                      }
  204.          $C4/$76/$0C/                  {                les     si,[bp+12]     ; point to string          }
  205.          $46/                          {                inc     si             ; point to first char      }
  206.          $BB/$40/$00/                  {                mov     bx,40h         ; check video card type    }
  207.          $8E/$C3/                      {                mov     es,bx          ; current column setting   }
  208.          $26/$F7/$26/$4A/$00/          {                mul     es:4Ah         ; set card mode            }
  209.          $03/$F8/                      {                add     di,ax                                     }
  210.          $D1/$E7/                      {                shl     di,1           ; attribute byte           }
  211.          $26/$8B/$16/$63/$00/          {                mov     dx,es:63h                                 }
  212.          $83/$C2/$06/                  {                add     dx,6           ; point to status port     }
  213.          $B8/$00/$B8/                  {                mov     ax,0B800H      ; first try color card     }
  214.          $26/$8B/$1E/$10/$00/          {                mov     bx,es:10h      ; check for card type      }
  215.          $81/$E3/$30/$00/              {                and     bx,30H                                    }
  216.          $83/$FB/$30/                  {                cmp     bx,30H         ; test for mono card       }
  217.          $75/$03/                      {                jne     setcard                                   }
  218.          $B8/$00/$B0/                  {                mov     ax,0B000H      ; else is a mono card      }
  219.          $8E/$C0/                      {  setcard:      mov     es,ax          ; point es to video        }
  220.          $EC/                          {  testlow:      in      al,dx          ; get status               }
  221.          $A8/$01/                      {                test    al,1           ; is it low ?              }
  222.          $75/$FB/                      {                jnz     testlow        ; no, keep checking        }
  223.          $FA/                          {                cli                    ; turn off interrupts      }
  224.          $EC/                          {  testhi:       in      al,dx          ; get status               }
  225.          $A8/$01/                      {                test    al,1           ; is it high ?             }
  226.          $74/$FB/                      {                jz      testhi         ; no, keep checking        }
  227.          $A4/                          {                movsb                  ; proper time to display   }
  228.          $47/                          {                inc     di             ; skip attribute byte      }
  229.          $E2/$F1/                      {                loop    testlow        ; end of string ?          }
  230.          $FB);                         {                sti                    ; turn interrupts on       }
  231. End;   { SpeedPrint2 }
  232.  
  233.  
  234.  
  235. Procedure DrawWindow1(    BeginCol,
  236.                           BeginRow,
  237.                           EndCol,
  238.                           EndRow:Integer);
  239.  
  240. { This procedure draws a rectangular window with a single line border at the
  241.   location specified.
  242.  
  243.  ( BeginCol,BeginRow ) __________________________
  244.                       |                          |
  245.                       |                          |
  246.                       |          Window          |
  247.                       |                          |
  248.                       | _________________________|
  249.                                                    ( EndCol,EndRow) }
  250.  
  251. Var
  252.   I:Integer;                           { variable used to help build the horizontal borderline }
  253.   BorderLine:String[77];               { string variable used in storing the top and bottom borderline }
  254.  
  255. Begin   { DrawWindow1 }
  256.   Window(BeginCol,BeginRow,EndCol,EndRow);
  257.   ClrScr;
  258.   BorderLine:='';
  259.   For I:=BeginCol+2 To EndCol-2 Do
  260.       BorderLine:=BorderLine+Chr(196);
  261.   GotoXY(2,1);
  262.   Write(Chr(218),BorderLIne,Chr(191));
  263.   For I:=2 To EndRow-BeginRow Do
  264.     Begin
  265.       GotoXY(2,I);
  266.       Write(Chr(179));
  267.       GotoXY(EndCol-BeginCol,I);
  268.       Write(Chr(179));
  269.     End; { For I }
  270.   GotoXY(2,EndRow-BeginRow+1);
  271.   Write(Chr(192),BorderLine,Chr(217));
  272.   Window(1,1,80,25);
  273. End;    { DrawWindow1 }
  274.  
  275.  
  276.  
  277. Procedure DrawWindow2(    BeginCol,
  278.                           BeginRow,
  279.                           EndCol,
  280.                           EndRow:Integer);
  281.  
  282. { This procedure draws a rectangular window with a double line border at the
  283.   location specified.
  284.  
  285.  ( BeginCol,BeginRow ) ==========================
  286.                       ||                        ||
  287.                       ||                        ||
  288.                       ||         Window         ||
  289.                       ||                        ||
  290.                       ||                        ||
  291.                        ==========================  ( EndCol,EndRow) }
  292.  
  293. Var
  294.   I:Integer;                           { variable used to help build the horizontal borderline }
  295.   BorderLine:String[77];               { string variable used in storing the top and bottom borderline }
  296.  
  297. Begin   { DrawWindow2 }
  298.   Window(BeginCol,BeginRow,EndCol,EndRow);
  299.   ClrScr;
  300.   BorderLine:='';
  301.   For I:=BeginCol+2 To EndCol-2 Do
  302.       BorderLine:=BorderLine+Chr(205);
  303.   GotoXY(2,1);
  304.   Write(Chr(201),BorderLIne,Chr(187));
  305.   For I:=2 To EndRow-BeginRow Do
  306.     Begin
  307.       GotoXY(2,I);
  308.       Write(Chr(186));
  309.       GotoXY(EndCol-BeginCol,I);
  310.       Write(Chr(186));
  311.     End; { For I }
  312.   GotoXY(2,EndRow-BeginRow+1);
  313.   Write(Chr(200),BorderLine,Chr(188));
  314.   Window(1,1,80,25);
  315. End;    { DrawWindow2 }
  316.  
  317.  
  318.  
  319. Procedure ZoomWindow1(    BeginCol,
  320.                           BeginRow,
  321.                           EndCol,
  322.                           EndRow:Integer);
  323.  
  324. { This procedure zooms horizontally a window with a single line border onto
  325.   the monitor screen.  A similar procedure could be written for a pull-down
  326.   menu like is used on the Apple MacIntosh. }
  327.  
  328. Var
  329.   Row:Integer;                         { index to a screen row for displaying the vertical borders of the window }
  330.   BeginStepCol:Integer;                { variable denoting the left edge of the temporary zoom portion of the window }
  331.   EndStepCol:Integer;                  { variable denoting the right edge of the temporary zoom portion of the window }
  332.  
  333. Begin   { ZoomWindow1 }
  334.   BeginStepCol:=((BeginCol+EndCol) Div 2)-1;
  335.   EndStepCol:=(BeginCol+EndCol) Div 2;
  336.   Window(BeginStepCol,BeginRow,EndStepCol,EndRow);
  337.   ClrScr;
  338.   Window(1,1,80,25);
  339.   GotoXY(BeginStepCol,BeginRow);
  340.   Write(Chr(196),Chr(196));
  341.   GotoXY(BeginStepCol,EndRow);
  342.   Write(Chr(196),Chr(196));
  343.   Repeat
  344.     BeginStepCol:=BeginStepCol-2;
  345.     EndStepCol:=EndStepCol+2;
  346.     Window(BeginStepCol,BeginRow,BeginStepCol+1,EndRow);
  347.     ClrScr;
  348.     Window(1,1,80,25);
  349.     GotoXY(BeginStepCol,BeginRow);
  350.     Write(Chr(196),Chr(196));
  351.     GotoXY(BeginStepCol,EndRow);
  352.     Write(Chr(196),Chr(196));
  353.     Window(EndStepCol-1,BeginRow,EndStepCol,EndRow);
  354.     ClrScr;
  355.     Window(1,1,80,25);
  356.     GotoXY(EndStepCol-1,BeginRow);
  357.     Write(Chr(196),Chr(196));
  358.     GotoXY(EndStepCol-1,EndRow);
  359.     Write(Chr(196),Chr(196));
  360.   Until (BeginStepCol-3<=BeginCol) Or (EndStepCol+3>=EndCol);
  361.   Window(BeginCol,BeginRow,BeginStepCol-1,EndRow);
  362.   ClrScr;
  363.   Window(1,1,80,25);
  364.   GotoXY(BeginCol+1,BeginRow);
  365.   Write(Chr(218),Chr(196),Chr(196),Chr(196));
  366.   GotoXY(BeginCol+1,EndRow);
  367.   Write(Chr(192),Chr(196),Chr(196),Chr(196));
  368.   For Row:=BeginRow+1 To EndRow-1 Do
  369.     Begin
  370.       GotoXY(BeginCol+1,Row);
  371.       Write(Chr(179));
  372.     End; { For Row }
  373.   Window(EndStepCol+1,BeginRow,EndCol,EndRow);
  374.   ClrScr;
  375.   Window(1,1,80,25);
  376.   GotoXY(EndCol-4,BeginRow);
  377.   Write(Chr(196),Chr(196),Chr(196),Chr(191));
  378.   GotoXY(EndCol-4,EndRow);
  379.   Write(Chr(196),Chr(196),Chr(196),Chr(217));
  380.   For Row:=BeginRow+1 To EndRow-1 Do
  381.     Begin
  382.       GotoXY(EndCol-1,Row);
  383.       Write(Chr(179));
  384.     End; { For Row }
  385. End;    { ZoomWindow1 }
  386.  
  387.  
  388.  
  389. Procedure ZoomWindow2(    BeginCol,
  390.                           BeginRow,
  391.                           EndCol,
  392.                           EndRow:Integer);
  393.  
  394. { This procedure zooms horizontally a window with a double line border onto
  395.   the monitor screen.  A similar procedure could be written for a pull-down
  396.   menu like is used on the Apple MacIntosh. }
  397.  
  398. Var
  399.   Row:Integer;                         { index to a screen row for displaying the vertical borders of the window }
  400.   BeginStepCol:Integer;                { variable denoting the left edge of the temporary zoom portion of the window }
  401.   EndStepCol:Integer;                  { variable denoting the right edge of the temporary zoom portion of the window }
  402.  
  403. Begin   { ZoomWindow2 }
  404.   BeginStepCol:=((BeginCol+EndCol) Div 2)-1;
  405.   EndStepCol:=(BeginCol+EndCol) Div 2;
  406.   Window(BeginStepCol,BeginRow,EndStepCol,EndRow);
  407.   ClrScr;
  408.   Window(1,1,80,25);
  409.   GotoXY(BeginStepCol,BeginRow);
  410.   Write(Chr(205),Chr(205));
  411.   GotoXY(BeginStepCol,EndRow);
  412.   Write(Chr(205),Chr(205));
  413.   Repeat
  414.     BeginStepCol:=BeginStepCol-2;
  415.     EndStepCol:=EndStepCol+2;
  416.     Window(BeginStepCol,BeginRow,BeginStepCol+1,EndRow);
  417.     ClrScr;
  418.     Window(1,1,80,25);
  419.     GotoXY(BeginStepCol,BeginRow);
  420.     Write(Chr(205),Chr(205));
  421.     GotoXY(BeginStepCol,EndRow);
  422.     Write(Chr(205),Chr(205));
  423.     Window(EndStepCol-1,BeginRow,EndStepCol,EndRow);
  424.     ClrScr;
  425.     Window(1,1,80,25);
  426.     GotoXY(EndStepCol-1,BeginRow);
  427.     Write(Chr(205),Chr(205));
  428.     GotoXY(EndStepCol-1,EndRow);
  429.     Write(Chr(205),Chr(205));
  430.   Until (BeginStepCol-3<=BeginCol) Or (EndStepCol+3>=EndCol);
  431.   Window(BeginCol,BeginRow,BeginStepCol-1,EndRow);
  432.   ClrScr;
  433.   Window(1,1,80,25);
  434.   GotoXY(BeginCol+1,BeginRow);
  435.   Write(Chr(201),Chr(205),Chr(205),Chr(205));
  436.   GotoXY(BeginCol+1,EndRow);
  437.   Write(Chr(200),Chr(205),Chr(205),Chr(205));
  438.   For Row:=BeginRow+1 To EndRow-1 Do
  439.     Begin
  440.       GotoXY(BeginCol+1,Row);
  441.       Write(Chr(186));
  442.     End; { For Row }
  443.   Window(EndStepCol+1,BeginRow,EndCol,EndRow);
  444.   ClrScr;
  445.   Window(1,1,80,25);
  446.   GotoXY(EndCol-4,BeginRow);
  447.   Write(Chr(205),Chr(205),Chr(205),Chr(187));
  448.   GotoXY(EndCol-4,EndRow);
  449.   Write(Chr(205),Chr(205),Chr(205),Chr(188));
  450.   For Row:=BeginRow+1 To EndRow-1 Do
  451.     Begin
  452.       GotoXY(EndCol-1,Row);
  453.       Write(Chr(186));
  454.     End; { For Row }
  455. End;    { ZoomWindow2 }
  456.  
  457.  
  458.  
  459. Procedure DrawHorizWindowLine1(    BeginCol,
  460.                                    BeginRow,
  461.                                    EndCol:Integer);
  462.  
  463. { This procedure draws a singular horizontal line in the interior of a
  464.   rectangular window which has a single line border.
  465.  
  466.      ( BeginCol,BeginRow )
  467.                           |--------------------------|
  468.                                                       ( EndCol )    }
  469.  
  470. Begin   { DrawHorizWindowLine1 }
  471.   GotoXY(BeginCol,BeginRow);
  472.   Write(' ',Chr(195));
  473.   While WhereX<EndCol-1 Do
  474.       Write(Chr(196));
  475.   Write(Chr(180),' ');
  476. End;    { DrawHorizWindowLine1 }
  477.  
  478.  
  479.  
  480. Procedure DrawHorizWindowLine2(    BeginCol,
  481.                                    BeginRow,
  482.                                    EndCol:Integer);
  483.  
  484. { This procedure draws a singular horizontal line in the interior of a
  485.   rectangular window which has a double line border.
  486.  
  487.      ( BeginCol,BeginRow )
  488.                           ||------------------------||
  489.                                                       ( EndCol )    }
  490.  
  491. Begin   { DrawHorizWindowLine2 }
  492.   GotoXY(BeginCol,BeginRow);
  493.   Write(' ',Chr(199));
  494.   While WhereX<EndCol-1 Do
  495.     Write(Chr(196));
  496.   Write(Chr(182),' ');
  497. End;    { DrawHorizWindowLine2 }
  498.  
  499.  
  500.  
  501. Procedure WriteCenterText(    Row       :Integer;
  502.                               TextString:WorkString);
  503.  
  504. { This procedure centers and writes a string of text at a given row on the
  505.   monitor screen. }
  506.  
  507. Begin   { WriteCenterText }
  508.   GotoXY(40-((Length(TextString)) div 2),Row);
  509.   Write(TextString);
  510. End;    { WriteCenterText }
  511.  
  512.  
  513.  
  514. Procedure SetCursorSize(    Top,
  515.                             Bottom:Integer);
  516.  
  517. { This procedure is used to change the current cursor size.  Top corresponds to
  518.   the top of the cursor block.  Bottom corresponds to the bottom of the cursor
  519.   block.  IBM specific.
  520.  
  521.   Typical IBM visible cursor blocks follow:
  522.  
  523.            MONOCHROME         COLOR         ENHANCED COLOR
  524.  
  525.   Top        0-||||           0-||||           0-||||
  526.                ||||             ||||             ||||
  527.                ||||             ||||             ||||
  528.   Bottom    13-||||           7-||||           8-||||   }
  529.  
  530. Begin   { SetCursorSize }
  531.   InLine($8A/$6E/$06/                  {   mov   ch,[bp+06]   }
  532.          $8A/$4E/$04/                  {   mov   cl,[bp+04]   }
  533.          $B4/$1F/                      {   mov   ah,1F        }
  534.          $22/$EC/                      {   and   ch,ah        }
  535.          $22/$CC/                      {   and   cl,ah        }
  536.          $B4/$01/                      {   mov   ah,01        }
  537.          $CD/$10);                     {   int   10           }
  538. End;    { SetCursorSize }
  539.  
  540.  
  541.  
  542. Procedure HideBlinkingCursor;
  543.  
  544. { This procedure hides the blinking cursor.  IBM specific. }
  545.  
  546. Begin   { HideBlinkingCursor }
  547.   InLine($B9/$0F00/                    {   mov   cx,0F00       ; turn cursor off  }
  548.          $B4/$01/                      {   mov   ah,01         ; cursor type      }
  549.          $CD/$10);                     {   int   10            ; screen interrupt }
  550. End;    { HideBlinkingCursor }
  551.  
  552.  
  553.  
  554. Procedure ShowBlinkingCursor;
  555.  
  556. { This procedure first determines what type of monitor is being used.  It
  557.   then sets the proper visible cursor for that monitor.  IBM specific. }
  558.  
  559. Begin   { ShowBlinkingCursor }
  560.   If MonitorType=7 Then  { monochrome adapter }
  561.     InLine($B9/$0C0D/                  {   mov   cx,0C0D       ; turn monochrome cursor on  }
  562.            $B4/$01/                    {   mov   ah,01         ; cursor type                }
  563.            $CD/$10)                    {   int   10            ; screen interrupt           }
  564.   Else { color or enhanced color adapter }
  565.     InLine($B9/$0607/                  {   mov   cx,0F00       ; turn color cursor on       }
  566.            $B4/$01/                    {   mov   ah,01         ; cursor type                }
  567.            $CD/$10);                   {   int   10            ; screen interrupt           }
  568. End;    { ShowBlinkingCursor }
  569.  
  570.  
  571.  
  572. Procedure InitTextScreenPointers;
  573.  
  574. { This procedure initializes the screen pointers to Nil that are used in the
  575.   storing of temporary text screens in the heap.  Temporary text screens are
  576.   stored, for example, just before a help screen is displayed on the monitor
  577.   screen. }
  578.  
  579. Var
  580.   Number:Integer;                      { an index for the TextScreen array }
  581.  
  582. Begin   { InitTextScreenPointers }
  583.   For Number:=1 To MAX_NUM_OF_TEXT_SCREENS Do
  584.     TextScreen[Number]:=Nil;
  585. End;    { InitTextScreenPointers }
  586.  
  587.  
  588.  
  589. Procedure StoreTextScreen(    Number:Integer);
  590.  
  591. { This procedure is called to store the currently displayed video image into
  592.   the heap.  The currently displayed screen is not affected by this storage
  593.   process.  You store the screen under a number and thus can have many screens
  594.   stored in the heap for instant display when required.  You can even animate
  595.   the screen using this process.  This is all possible due to Turbo's 'Move'
  596.   procedure which provides a very fast way of moving a block of memory.
  597.  
  598.   Another example:
  599.  
  600.            User has entered a help command so that a help screen will
  601.            be displayed onto the screen.
  602.  
  603.               1. Store the currently displayed video image.
  604.               2. Write a help screen onto the currently displayed video image.
  605.               3. Wait until the user has read the help screen and has replied
  606.                  to return to previous work.
  607.               4. Restore the previously stored image.
  608.  
  609.   This procedure could be easily modified to recall graphic screens.  Simply
  610.   define the screen size from 4096 to 16384 in the constant declaration in the
  611.   program header. }
  612.  
  613. Var
  614.   ColorMonitorImage:Array[1..TEXT_SCREEN_SIZE] of Byte Absolute $B800:$0000;
  615.     { an overlayed map of the color video memory addresses }
  616.   MonoMonitorImage:Array[1..TEXT_SCREEN_SIZE] of Byte Absolute $B000:$0000;
  617.     { an overlayed map of the monochrome video memory addresses }
  618.  
  619. Begin   { StoreTextScreen }
  620.   New(TextScreen[Number]);                                              { allocate screen image space in the heap to }
  621.                                                                         { store currently displayed image }
  622.   If MonitorType=7 Then { monochrome adapter }
  623.     Move(MonoMonitorImage,TextScreen[Number]^.Image,TEXT_SCREEN_SIZE)   { store currently displayed video image into }
  624.                                                                         { the heap as TextScreen[Number] }
  625.   Else
  626.     Begin { color or enhanced color adapter }
  627.       Repeat Until ((Port[$3DA] And 8)=8);                              { wait for video retrace to end }
  628.       Port[$3D8]:=1;                                                    { temporarily haly video retrace }
  629.       Move(ColorMonitorImage,TextScreen[Number]^.Image,TEXT_SCREEN_SIZE); { store currently displayed video image into }
  630.                                                                         { the heap as TextScreen[Number] }
  631.       Port[$3D8]:=9;                                                    { restore video retrace }
  632.     End; { color or enhanced color adapter }
  633. End;    { StoreTextScreen }
  634.  
  635.  
  636.  
  637. Procedure RecallTextScreen(    Number:Integer);
  638.  
  639. { This procedure is called to recall TextScreen[Number] from the heap and
  640.   display onto the monitor.  You must first store a screen under a number
  641.   before you can recall it.  Using this process you can even animate the
  642.   screen.  This is all possible due to Turbo's 'Move' procedure which provides
  643.   a very fast way of moving a block of memory.
  644.  
  645.   Another example:
  646.  
  647.            User has entered a help command so that the program will display
  648.            a help window onto the screen.  How do you restore the previous
  649.            screen without reconstructing the whole screen ?  Do the following:
  650.  
  651.               1. Store the currently displayed video image.
  652.               2. Write a help window onto the currently displayed video image.
  653.               3. Wait until the user has read the help window and has replied
  654.                  to return to previous work.
  655.               4. Restore the previously stored screen image.
  656.  
  657.   This procedure could be easily modified to recall graphic screens.  Simply
  658.   define the screen size from 4096 to 16384 in the constant declaration in the
  659.   program header. }
  660.  
  661. Var
  662.   ColorMonitorImage:Array[1..TEXT_SCREEN_SIZE] of Byte Absolute $B800:$0000;
  663.     { an overlayed map of the color video memory addresses }
  664.   MonoMonitorImage:Array[1..TEXT_SCREEN_SIZE] of Byte Absolute $B000:$0000;
  665.     { an overlayed map of the monochrome video memory addresses }
  666.  
  667. Begin   { RecallTextScreen }
  668.   If MonitorType=7 Then { monochrome adapter }
  669.     Move(TextScreen[Number]^.Image,MonoMonitorImage,TEXT_SCREEN_SIZE)   { recall TextScreen[Number] from the heap }
  670.                                                                         { and display }
  671.   Else
  672.     Begin { color or enhanced color adapter }
  673.       Repeat Until ((Port[$3DA] And 8)=8);                              { wait for video retrace to end }
  674.       Port[$3D8]:=1;                                                    { temporarily haly video retrace }
  675.       Move(TextScreen[Number]^.Image,ColorMonitorImage,TEXT_SCREEN_SIZE); { recall TextScreen[Number] from the heap and }
  676.                                                                         { display }
  677.       Port[$3D8]:=9;                                                    { restore video retrace }
  678.     End; { color or enhanced color adapter }
  679.   Dispose(TextScreen[Number]);                                          { remove previously stored screen from heap }
  680.                                                                         { thus freeing up memory in the heap }
  681.   TextScreen[Number]:=Nil;
  682. End;    { RecallTextScreen }
  683.  
  684.  
  685.  
  686. Procedure WriteScreenPageToFile(    ScreenPageFileName:WorkString);
  687.  
  688. { This procedure is used to write the currently displayed text screen to a
  689.   screen file of the passed file name.  The text screen pages are stored under
  690.   screen files titled '________.COL(or MON)' where COL stands for color and
  691.   MON stands for monochrome.  The currently displayed screen is not affected by
  692.   this process.
  693.  
  694.   This procedure can be used to make your program appear very professional
  695.   looking with rapid screen page displays.  There will no longer be any need
  696.   to construct a text screen with Write statements everytime a screen is to be
  697.   displayed.  By storing the screen pages ahead of time in files and then
  698.   having the application program read the screen pages out of their files
  699.   and storing them in the heap, it is very easy to instantly display various
  700.   text screen pages.  In addition, your application program no longer
  701.   requires the code to construct the screen pages since the screen pages are
  702.   are stored in the heap.  This gives you more room to write code for your
  703.   application.
  704.  
  705.   You should run this procedure twice for each screen page, once on a
  706.   monochrome machine and once again on a color machine.  The reason for this
  707.   is that the colors are different on the two machines and the video buffers
  708.   reside at different locations, also.  You can circumnavigate having to use
  709.   two machines by simply adjusting the colors for the file you wish to write
  710.   and making sure to copy from the right video buffer location.  The function
  711.   MonitorType is used to determine the type of monitor being used.  It is
  712.   used in the initialization module to set the proper screen colors and other
  713.   items.  You can trick it into thinking you have the other type of display
  714.   to help set up screen colors but then rewrite this procedure so that this
  715.   procedure copies from the proper video buffer location.
  716.  
  717.   This procedure is not meant to be used in the application program.  It is
  718.   meant to be used during program development once the screen pages have been
  719.   finalized.  The re-display of the stored text screens should then be
  720.   accomplished in the application program with the two procedures
  721.   'ReadScreenPagesFromFiles' and 'DisplayScreenPage(PageNumber)' found below.
  722.  
  723.   IBM Specific. }
  724.  
  725. Const
  726.   BLOCK_SIZE=32;                                                { the number of 128-byte blocks to be written out }
  727.  
  728. Var
  729.   ColorMonitorImage:Array[1..TEXT_SCREEN_SIZE] of Byte Absolute $B800:$0000;
  730.     { an overlayed map of the color video memory addresses }
  731.   MonoMonitorImage:Array[1..TEXT_SCREEN_SIZE] of Byte Absolute $B000:$0000;
  732.     { an overlayed map of the monochrome video memory addresses }
  733.   TextScreenFile:File;                                          { untyped screen file }
  734.  
  735. Begin   { WriteScreenPageToFile }
  736.   If MonitorType=3 Then { color adapter and color monitor }
  737.     Begin { color text screen page }
  738.       Assign(TextScreenFile,ScreenPageFileName+'.COL');         { assign disk file }
  739.       Rewrite(TextScreenFile);                                  { open the file for writing }
  740.       BlockWrite(TextScreenFile,ColorMonitorImage,BLOCK_SIZE);  { write untyped data to file }
  741.       Close(TextScreenFile);                                    { close the file }
  742.     End { color text screen page }
  743.   Else
  744.     If MonitorType=7 Then { monochrome adapter and monochrome monitor }
  745.       Begin { monochrome text screen page }
  746.         Assign(TextScreenFile,ScreenPageFileName+'.MON');         { assign disk file }
  747.         Rewrite(TextScreenFile);                                  { open the file for writing }
  748.         BlockWrite(TextScreenFile,MonoMonitorImage,BLOCK_SIZE);   { write untyped data to file }
  749.         Close(TextScreenFile);                                    { close the file }
  750.       End { monochrome text screen page }
  751.     Else { color adapter and monochrome monitor }
  752.       Begin { monochrome text screen page }
  753.         Assign(TextScreenFile,ScreenPageFileName+'.MON');         { assign disk file }
  754.         Rewrite(TextScreenFile);                                  { open the file for writing }
  755.         BlockWrite(TextScreenFile,ColorMonitorImage,BLOCK_SIZE);  { write untyped data to file }
  756.         Close(TextScreenFile);                                    { close the file }
  757.       End; { monochrome text screen page }
  758. End;    { WriteScreenPageToFile }
  759.  
  760.  
  761.  
  762. Procedure ReadScreenPageFromFile(    ScreenPageFileName:WorkString;
  763.                                  Var ScreenPageImage   :TextScreenPtr);
  764.  
  765. { This procedure reads the stored text screen pages from the passed file name
  766.   (which were generated using the procedure WriteScreenPageToFile) and passes
  767.   the ScreenPageImage back to the calling routine.  The text screen pages are
  768.   stored under screen files titled '________.COL(or MON)' where COL stands
  769.   for color and MON stands for monochrome.  The currently displayed screen is
  770.   not affected by this process.
  771.  
  772.   This procedure can be used to make your program appear very professional
  773.   looking with rapid screen page displays.  There will no longer be any need
  774.   to construct a text screen with Write statements everytime a screen is to
  775.   be displayed.  By storing the screen pages ahead of time in files and then
  776.   having the application program read the screen pages out of their files
  777.   and storing them in the heap, it is very easy to instantly display various
  778.   text screen pages.  In addition, your application program no longer
  779.   requires the code to construct the screen pages since the screen pages are
  780.   are stored in the heap.  This gives you more room to write code for your
  781.   application.
  782.  
  783.   The re-display of the stored text screens is accomplished in the
  784.   application program by using this procedure and the procedure
  785.   'DisplayScreenPage'.
  786.  
  787.   IBM Specific. }
  788.  
  789. Const
  790.   BLOCK_SIZE=32;                                            { the number of 128-byte blocks to be read in }
  791.  
  792. Var
  793.   TextScreenFile:File;                                      { untyped screen file }
  794.  
  795. Begin   { ReadScreenPageFromFiles }
  796.   New(ScreenPageImage);                                     { allocate screen image space in the heap }
  797.   If MonitorType=3 Then { color adapter and color monitor }
  798.     Begin { color text screen page }
  799.       Assign(TextScreenFile,ScreenPageFileName+'.COL');     { assign disk file }
  800.       Reset(TextScreenFile);                                { open the file for reading }
  801.       BlockRead(TextScreenFile,ScreenPageImage^.Image,BLOCK_SIZE); { read untyped data from file }
  802.       Close(TextScreenFile);                                { close the file }
  803.     End { color text screen page }
  804.   Else { monochrome adapter or color adapter and monochrome monitor }
  805.     Begin { monochrome text screen page }
  806.       Assign(TextScreenFile,ScreenPageFileName+'.MON');     { assign disk file }
  807.       Reset(TextScreenFile);                                { open the file for reading }
  808.       BlockRead(TextScreenFile,ScreenPageImage^.Image,BLOCK_SIZE); { read untyped data from file }
  809.       Close(TextScreenFile);                                { close the file }
  810.     End; { monochrome text screen page }
  811. End;    { ReadScreenPageFromFiles }
  812.  
  813.  
  814.  
  815. Procedure DisplayScreenPage(    ScreenPageImage:TextScreenPtr);
  816.  
  817. { This procedure is called to recall the passed ScreenPageImage from the
  818.   heap and display the screen page image onto the monitor.  The screen page
  819.   must have first been read into the heap by the procedure
  820.   'ReadScreenPagesFromFiles' before this procedure can be used.  The stored
  821.   screen page is uneffected by this process.
  822.  
  823.   This procedure can be used to make your program appear very professional
  824.   looking with rapid screen page displays.  There will no longer be any need
  825.   to construct a text screen with Write statements everytime a screen is to
  826.   be displayed.  By storing the screen pages ahead of time in files and then
  827.   having the application program read the screen pages out of their files
  828.   and storing them in the heap, it is very easy to instantly display various
  829.   text screen pages.  In addition, your application program no longer
  830.   requires the code to construct the screen pages since the screen pages are
  831.   are stored in the heap.  This gives you more room to write code for your
  832.   application.
  833.  
  834.   IBM Specific. }
  835.  
  836. Var
  837.   ColorMonitorImage:Array[1..TEXT_SCREEN_SIZE] of Byte Absolute $B800:$0000;
  838.     { an overlayed map of the color video memory addresses }
  839.   MonoMonitorImage:Array[1..TEXT_SCREEN_SIZE] of Byte Absolute $B000:$0000;
  840.     { an overlayed map of the monochrome video memory addresses }
  841.  
  842. Begin   { DisplayScreenPage }
  843.   If MonitorType=7 Then { monochrome adapter and monchrome monitor }
  844.     Move(ScreenPageImage^.Image,MonoMonitorImage,TEXT_SCREEN_SIZE)   { recall ScreenPage[PageNumber] from the heap }
  845.                                                               { and display on monochrome monitor }
  846.   Else 
  847.     Begin { color adapter and color monitor or monochrome monitor }
  848.       Repeat Until ((Port[$3DA] And 8)=8);                              { wait for video retrace to end }
  849.       Port[$3D8]:=1;                                                    { temporarily haly video retrace }
  850.       Move(ScreenPageImage^.Image,ColorMonitorImage,TEXT_SCREEN_SIZE);  { recall ScreenPage[PageNumber] from the heap
  851.                                                                         { and display on color or monochrome monitor }
  852.       Port[$3D8]:=9;                                                    { restore video retrace }
  853.     End;  { color adapter and color monitor or monochrome monitor }
  854. End;    { DisplayScreenPage }
  855.  
  856.  
  857.  
  858. Procedure SoundError;
  859.  
  860. { This procedure makes a sound when an illegal character has been entered. }
  861.  
  862. Begin   { SoundError }
  863.   Sound(230);Delay(50);NoSound;
  864. End;    { SoundError }
  865.  
  866.  
  867.  
  868. Procedure SoundAttention;
  869.  
  870. { This procedure makes a sound to get the user's attention. }
  871.  
  872. Begin   { SoundAttention }
  873.   Sound(630);Delay(30);NoSound;Delay(40);Sound(630);Delay(30);NoSound;
  874. End;    { SoundAttention }
  875.  
  876.  
  877.  
  878. Procedure WaitUntilKeypressed;
  879.  
  880. { This procedure is called, for example, when an overlayed window is displayed
  881.   and tha program is waiting for the user to strike any key.  Then the
  882.   overlayed window should be removed.  This procedure is necessary since
  883.   a key that the user may strike may generate a two character code.  This
  884.   procedure deals with that by recognizing that two characters were generated
  885.   and that it should ignore both of them. }
  886.  
  887. Var
  888.   KeyboardEntry:Char;                  { char variable used to absorb user's keystroke }
  889.  
  890. Begin   { WaitUntilKeypressed }
  891.   Read(Kbd,KeyboardEntry);
  892.   If (KeyboardEntry=Chr(27)) And Keypressed Then
  893.     Read(Kbd,KeyboardEntry);
  894. End;    { WaitUntilKeypressed }
  895.  
  896.  
  897.  
  898. Function Date1:WorkString;
  899.  
  900. { This function determines the current date by making a DOS call to the
  901.   computer's clock.  This function returns a string with the current day
  902.   of the week, the month, the date, and the year.  An example date that
  903.   might be returned follows:
  904.  
  905.                       Thu  Oct. 10, 1985
  906.  
  907.   This function can be easily modified so that the entire day name and
  908.   month name is returned if so desired.  IBM specific.  }
  909.  
  910. Const
  911.   DaysOfTheWeek:  String[21]='SunMonTueWedThuFriSat';                { string constant for week days }
  912.   MonthsOfTheYear:String[36]='JanFebMarAprMayJunJulAugSepOctNovDec'; { string constant for calendar months }
  913.  
  914. Type
  915.   RecordOfRegisters=
  916.     Record
  917.       AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags:Integer  { Record for storing register values }
  918.     End; { RecordOfRegisters }
  919.  
  920. Var
  921.   Register:RecordOfRegisters;          { variable used in reading internal registers }
  922.   Day:String[2];                       { string used in determining calendar day from internal system clock }
  923.   Year:String[4];                      { string used in determining calendar year from internal system clock }
  924.  
  925. Begin   { Date1 }
  926.   Register.AX:=$2A00;                  { place function number $2A (Get Date) into register AX }
  927.   MsDos(Register);                     { invoke DOS interrupt $21 }
  928.   Str(Lo(Register.DX),Day);            { convert integer to string }
  929.   Str(Register.CX,Year);               { convert integer to string }
  930.   Date1:=Copy(DaysOfTheWeek,3*Lo(Register.AX)+1,3)+'  '+
  931.          Copy(MonthsOfTheYear,3*Hi(Register.DX)-2,3)+'. '+
  932.          Day+', '+Year;
  933. End;    { Date1 }
  934.  
  935.  
  936.  
  937. Function Date2:WorkString;
  938.  
  939. { This function determines the current date by making a DOS call to the
  940.   computer's clock.  IBM specific.  An example date that might be returned
  941.   follows:
  942.  
  943.                       8/10/1985                   }
  944.  
  945. Type
  946.   RecordOfRegisters=
  947.     Record
  948.       AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags:Integer  { Record for storing register values }
  949.     End; { RecordOfRegisters }
  950.  
  951. Var
  952.   Register:RecordOfRegisters;          { variable used in reading internal registers }
  953.   Month,Day:String[2];                 { string used in determining calendar day from internal system clock }
  954.   Year:String[4];                      { string used in determining calendar year from internal system clock }
  955.  
  956. Begin   { Date2 }
  957.   Register.AX:=$2A00;                  { place function number $2A (Get Date) into register AX }
  958.   MsDos(Register);                     { invoke DOS interrupt $21 }
  959.   With Register Do
  960.     Begin
  961.       Str(CX,Year);                    { convert integer to string }
  962.       Str(DX Mod 256,Day);             { convert integer to string }
  963.       Str(DX Shr 8,Month);             { convert integer to string }
  964.     End; { With Register }
  965.   Date2:=Month+'/'+Day+'/'+Year;
  966. End;    { Date2 }
  967.  
  968.  
  969.  
  970. Function Time:WorkString;
  971.  
  972. { This function determines the current time by making a DOS call to the
  973.   computer's clock.  IBM specific.  An example time that might be returned
  974.   follows:
  975.  
  976.                       12:05:32 PM                 }
  977.  
  978. Type
  979.   RecordOfRegisters=
  980.     Record
  981.       AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags:Integer  { Record for storing register values }
  982.     End; { RecordOfRegisters }
  983.  
  984. Var
  985.   Register:RecordOfRegisters;          { variable used in reading internal registers }
  986.   Hour:String[2];                      { string used in determining hours from internal system clock }
  987.   Minutes:String[2];                   { string used in determining minutes from internal system clock }
  988.   Seconds:String[5];                   { string used in determining seconds from internal system clock }
  989.  
  990. Begin   { Time }
  991.   Register.AX:=$2C00;                  { place function number $2C (Get Time) into register AX }
  992.   Intr($21,Register);                  { invoke DOS interrupt $21 }
  993.   With Register Do
  994.     Begin
  995.       Str(CX Shr 8,Hour);              { convert integer to string }
  996.       Str(CX Mod 256,Minutes);         { convert integer to string }
  997.       Str(DX Shr 8,Seconds);           { convert integer to string }
  998.       If Length(Minutes)=1 Then        { check if less than 10 minutes }
  999.         Minutes:='0'+Minutes;
  1000.       If Length(Seconds)=1 Then        { check if less than 10 seconds }
  1001.         Seconds:='0'+Seconds;
  1002.       If (CX Shr 8)>12 Then            { check if in afternoon }
  1003.         Begin
  1004.           Str((CX Shr 8)-12,Hour);     { convert to 12 hour time }
  1005.           Seconds:=Seconds+' PM';
  1006.         End { If CX }
  1007.       Else
  1008.         Begin
  1009.           If (CX Shr 8)=0 Then         { check if immediately after 12:00 midnight }
  1010.             Hour:='12';
  1011.           If (CX Shr 8)=12 Then        { check if immediately after 12:00 noon }
  1012.             Seconds:=Seconds+' PM'
  1013.           Else                         { else it's still morning }
  1014.             Seconds:=Seconds+' AM';
  1015.         End { Else }
  1016.     End; { With Register }
  1017.   Time:=Hour+':'+Minutes+':'+Seconds;
  1018. End;    { Time }